#include <linux/cpu.h>
#include "os_compat.h"

#if LINUX_VERSION_CODE < KERNEL_VERSION(5,6,0)

#include <linux/device.h>
#include <linux/kallsyms.h>

typedef void (*lock_device_hotplug_fn)(void);
typedef void (*unlock_device_hotplug_fn)(void);
typedef int (*device_offline_fn)(struct device *dev);
typedef int (*device_online_fn)(struct device *dev);

static lock_device_hotplug_fn lock_device_hotplug_func;
static unlock_device_hotplug_fn unlock_device_hotplug_func;
static device_offline_fn device_offline_func;
static device_online_fn device_online_func;

int os_compat_add_cpu(unsigned int cpu)
{
	int ret;

	lock_device_hotplug_func();
	ret = device_online_func(get_cpu_device(cpu));
	unlock_device_hotplug_func();

	return ret;
}

int os_compat_remove_cpu(unsigned int cpu)
{
	int ret;

	lock_device_hotplug_func();
	ret = device_offline_func(get_cpu_device(cpu));
	unlock_device_hotplug_func();

	return ret;
}

#define STR(name)	#name

#define GET_FUNC(name)								\
do {										\
	if (!name##_func) {							\
		name##_func = (name##_fn)kallsyms_lookup_name(STR(name));	\
		if (!name##_func) {						\
			pr_err("qtbox: lookup "STR(name)" failed\n");		\
			return -ENOENT;						\
		}								\
	}									\
} while (0)

int os_compat_init(void)
{
	GET_FUNC(lock_device_hotplug);
	GET_FUNC(unlock_device_hotplug);
	GET_FUNC(device_offline);
	GET_FUNC(device_online);

	return 0;
}

#else

int os_compat_add_cpu(unsigned int cpu)
{
	return add_cpu(cpu);
}

int os_compat_remove_cpu(unsigned int cpu)
{
	return remove_cpu(cpu);
}

int os_compat_init(void) { return 0; }

#endif
